# coding: utf-8
"""
Description: CI Result
version: 1.0.0
Author: peng2.zhang
Date: 2021-08-31 14:19:59
LastEditors: peng2.zhang
LastEditTime: 2021-08-31 14:20:00
Usage:
  ci_result allure --job_name=<job_name> --buildid=<buildid> --stage_id=<stage_id> --stage_name=<stage_name> [--bootstrap_servers=<bootstrap_servers> --kafka_topic=<kafka_topic>] --result_path=<result_path>
  ci_result junit --job_name=<job_name> --buildid=<buildid> --stage_id=<stage_id> --stage_name=<stage_name> [--bootstrap_servers=<bootstrap_servers> --kafka_topic=<kafka_topic>] --result_path=<result_path>
  ci_result sonar --job_name=<job_name> --buildid=<buildid> --stage_id=<stage_id> --stage_name=<stage_name> [--bootstrap_servers=<bootstrap_servers> --kafka_topic=<kafka_topic>]
  ci_result java_coverage --job_name=<job_name> --buildid=<buildid> --stage_id=<stage_id> --stage_name=<stage_name> [--bootstrap_servers=<bootstrap_servers> --kafka_topic=<kafka_topic>] --result_path=<result_path>
  ci_result collect_data [--language=<language>] --job_name=<job_name> --buildid=<buildid> --git_name=<git_name> [--weixin_token=<weixin_token> --git_commit_id=<git_commit_id> --git_branch_name=<git_branch_name> --git_token=<git_token> --eureka_app_name=<eureka_app_name>]
  ci_result -h | --help
  ci_result --version

Options:
  -h --help     帮助.
  -v --version     查看版本号.
  --language=<language>  language设置 [default: java].
  --git_name=<git_name>  git name设置.
  --git_commit_id=<git_commit_id>  git commit_id设置.
  --git_branch_name=<git_branch_name>  git branch_name设置 [default: origin/master].
  --git_token=<git_token>  git token设置.
  --eureka_app_name=<eureka_app_name>  eureka注册app_name设置.
  --job_name=<job_name>  job name设置.
  --buildid=<buildid>  buildid设置.
  --stage_id=<stage_id>  stage_id设置.
  --stage_name=<stage_name>  stage_name设置.
  --result_path=<result_path>  result_path设置.
  --weixin_token=<weixin_token>  weixin_token设置 [default 2eaf10d4-8b4d-46ee-be2a-988f988840b0].
  --kafka_topic=<kafka_topic>  kafka topic设置 [default: 127.0.0.1:9091].
  --bootstrap_servers=<bootstrap_servers>  bootstrap_servers设置 [default: test].
"""
import os
from urllib.parse import urljoin
from docopt import docopt
from ci_conclude.const import JenkinsUser, LanguageType
from ci_conclude.headline_conclude import HeadlineConclude
from ci_conclude.allure_conclude import AllureConclude
from ci_conclude.junit_conclude import JunitConclude
from ci_conclude.sonar_conclude import SonarConclude
from ci_conclude.coverage_java_conclude import CoverageJavaConclude
from ci_conclude.coverage_py_conclude import CoveragePyConclude
from ci_conclude.weixin import send_message
from ci_conclude.jenkins_manage import JenkinsManage
from ci_conclude.git_manage import GitManage
from ci_conclude.git_parser import GitParser
from ci_conclude.models.db import DBConnection
from ci_conclude.services.svc_api_wx_user import WxUserService
from ci_conclude.services.svc_api_jenkins_build_history import JenkinsBuildHistoryService

valid_items = ["headline", "sonar", "junit", "java_coverage", "python_coverage", "cxx_coverage", "allure"]


class CIResult:

    def __init__(self, language="java"):
        if language.lower() not in ["python", "java", "cxx"]:
            raise NotImplementedError()
        self.language = language.lower()
        self.language_type = LanguageType.get_id_by_name(self.language)
    
    def support_sonar(self):
        support = {
            "python": True,
            "java": True,
            "cxx": False,
        }.get(self.language)
        return support

    def support_coverage(self):
        support = {
            "python": True,
            "java": True,
            "cxx": False,
        }.get(self.language)
        return support
    
    @property
    def item_list(self):
        support = {
            "python": ["headline", "sonar", "junit", "python_coverage", "allure"],
            "java": ["headline", "sonar", "junit", "java_coverage", "allure"],
            # "cxx": ["headline", "sonar", "junit", "cxx_coverage", "allure"],
            "cxx": ["headline", "sonar"],
        }.get(self.language)
        return support

    def _getConcludeClass(self, item_name):
        SwitchConclude = {
            "headline": HeadlineConclude,
            "allure": AllureConclude,
            "junit": JunitConclude,
            "sonar": SonarConclude,
            "java_coverage": CoverageJavaConclude,
            "python_coverage": CoveragePyConclude,
        }.get(item_name)
        if not SwitchConclude:
            raise NotImplementedError()
        return SwitchConclude
    
    def _getConcludeParams(self, item_name, product="stdci", job_name="", buildid=""):
        """
            (result_path, stage_id, stage_name)
        """
        switchParams = {
            "headline": (None, 0, "构建信息"),
            "allure": ("opt/Report/result.json", 9, "ZentaoTest"),
            "junit": ("opt/Report/result.xml", 8, "UnitTest"),
            "sonar": (None, 2, "Sonar"),
            "java_coverage": ("opt/Report/jacoco.xml", 8, "Coverage"),
            "python_coverage": ("opt/Report/pycov.xml", 8, "Coverage"),
            "cxx_coverage": ("opt/Report/cxxcov.xml", 8, "Coverage"),
        }.get(item_name)
        result_path, stage_id, stage_name = switchParams
        if result_path:
            namespace = self.change_variable_name(stage_name)
            path = os.path.join("autobuildtestline", namespace, product, job_name, buildid, result_path)
            result_path = urljoin(f"http://ecarx.stor.ecarx.com.cn", path)
        # if item_name == "junit":
        #     from code import interact
        #     interact(local=locals())
        return result_path, stage_id, stage_name
    
    def change_variable_name(self, listx):    
        """
            OneTwoThree --> one_two_three
        """
        listy = listx[0]
        for i in range(1,len(listx)):
            # listx[i] 直接copy 或 先加'_'再copy
            if listx[i].isupper() and not listx[i-1].isupper():# 加'_',当前为大写，前一个字母为小写
                listy+='_'
                listy+=listx[i]
            elif listx[i].isupper() and listx[i-1].isupper() and listx[i+1].islower():
                # 加'_',当前为大写，前一个字母为小写
                listy+='_'
                listy+=listx[i]
            else:
                listy+=listx[i]
        return listy.lower()
    
    def _getConcludeFailureReason(self, item_name):
        switchReason = {
            "allure": "未正确生成自动测试用例json结果",
            "junit": "未正确生成单元测试xml结果",
            "sonar": "当前代码库未查到sonar代码扫描结果" if self.support_sonar() else "当前语言暂时不支持sonar代码扫描",
            "java_coverage": "未正确生成单元测试覆盖结果",
            "python_coverage": "未正确生成单元测试覆盖结果",
            "cxx_coverage": "当前语言暂时不支持生成单元测试覆盖结果",
        }.get(item_name)
        return switchReason

    def _getConcludeResult(self, result_path=None):
        # SwitchConclude = self.SwitchConclude
        ciConclude = self.ciConclude
        # 获取result
        get_result_need_path = ciConclude._get_result_need_path
        if get_result_need_path:
            res = ciConclude.get_result(result_path)
        else:
            res = ciConclude.get_result()
        # 获取url
        get_url_need_path = ciConclude._get_url_need_path
        if get_url_need_path:
            url = ciConclude.get_url(result_path)
        else:
            url = ciConclude.get_url()
        # 基于result和url，获取weixin content
        content = ciConclude.format_to_weixin(res, url)
        return res, url, content

    def upload_result(self, item_name, job_name, buildid, stage_id, stage_name, bootstrap_servers, kafka_topic, result_path):
        # 获取对应item的CiConclude类
        self.SwitchConclude = self._getConcludeClass(item_name)
        # 初始化ciConclude类
        self.ciConclude = self.SwitchConclude(job_name, buildid, stage_id, stage_name, bootstrap_servers=bootstrap_servers, kafka_topic=kafka_topic)
        # 获取当前item测试结果
        res, url, content = self._getConcludeResult(result_path=result_path)
        # 上报kafka
        self.ciConclude.upload_result(url, res)

    def format_column(self, k, prefix=None):
        if prefix:
            return f"{prefix}_{k}"
        else:
            return k
    
    def collect_data(self, job_name, buildid, git_name, git_token="", git_commit_id="", git_branch_name="", eureka_app_name="", weixin_token=None):
        content_list = []
        # 获取Jenkins结果
        jenkinsManage = JenkinsManage(job_name=job_name, username=JenkinsUser.DEFAULT.NAME, password=JenkinsUser.DEFAULT.PASS)
        job_description = jenkinsManage.get_job_description(job_name)
        build_username = jenkinsManage.get_build_username(job_name, buildid)
        # build_usermobile = UserMobile.get_id_by_name(build_username)
        try:
            # gitManage = GitManage(git_name, git_commit_id=git_commit_id, git_branch_name=git_branch_name, git_token=git_token)
            # commit = gitManage.getLatestCommit()
            # committer_email = commit.committer_email
            # committer_name = committer_email.split("@")[0]
            gitParser = GitParser(job_name=job_name, build_id=buildid)
            committer_name = gitParser.git_commit_user
            committer_email = f"{committer_name}@ecarxgroup.com"
        except ValueError:
            committer_email = ''
            committer_name = ''
        mobile_list = WxUserService.get_mobiles([build_username, committer_name])
        title = f"# {job_description} \n ### **第{buildid}次构建CI测试结果**"
        kwargs = {"language_type": self.language_type}
        res_code_list = []
        for item_name in self.item_list:
            # 获取当前测试item的参数
            # (result_path, stage_id, stage_name)
            self.SwitchConclude = self._getConcludeClass(item_name)
            product = job_name.split("_")[0]
            switchParams = self._getConcludeParams(item_name, product, job_name, buildid)
            result_path, stage_id, stage_name = switchParams
            self.ciConclude = self.SwitchConclude(job_name, buildid, stage_id, stage_name, git_name=git_name, git_token=git_token, git_commit_id=git_commit_id, eureka_app_name=eureka_app_name, git_branch_name=git_branch_name)
            print(f"{item_name} result_path: {result_path}")
            # if item_name == "sonar":
            #     from code import interact
            #     interact(local=locals())

            res, url, content = self._getConcludeResult(result_path=result_path)
            print(f"{item_name} res: {res}\n url: {url}")
            res_code_list.append({"stage_name": stage_name, "code": res.get("code", 0)})
            sql_column_value = {self.format_column(k, res.get("prefix")):v for k,v in res.get("data", {}).items()}
            kwargs.update(sql_column_value)

            content_list.append(content)
        # 获取fail状态的item
        failure_items = [x for x in res_code_list if x["code"] < 0]
        # from code import interact
        # interact(local=locals())
        print(f"failure_items:{len(failure_items)}")
        if failure_items:
            failure_items_string = ",".join([x["stage_name"] for x in failure_items])
            title += f" <font color='warning'>：FAIL，抱歉本次构建失败，请关注{failure_items_string} Report！</font>"
        else:
            title += " <font color='info'>：PASS，恭喜本次构建通过！</font>"
        contents = "\n".join(content_list)
        contents = f"{title} \n {contents}"
        print(contents)
        if not weixin_token:
            weixin_token = "2eaf10d4-8b4d-46ee-be2a-988f988840b0"   # 2eaf10d4-8b4d-46ee-be2a-988f988840b0,张鹏测试组
        send_message(contents, weixin_token, msgtype="markdown")
        with open("jenkins_build_info.properties", "w") as f:
            for k, v in kwargs.items():
                f.write(f"{k}={v}\n")
        cmd = f"""qsctl cp jenkins_build_info.properties qs://autobuildtestline/ad-ci-jenkins/{product}/{job_name}/{buildid}/ -c ~/.qingstor_config.yaml"""
        command = os.popen(cmd)
        res = command.buffer.read().decode(encoding='utf8')
        send_message(f"请关注{job_description}CI构建结果", weixin_token, msgtype="text", mentioned_mobile_list=mobile_list, debug=True)
        JenkinsBuildHistoryService.create(**kwargs)

if __name__ == '__main__':
    arguments = docopt(__doc__, version="1.0.0")
    cases = [k for k, v in arguments.items() if not k.startswith("--") and v==True]
    # send_message(arguments["--content"], key=arguments["--key"], msgtype=arguments["--msgtype"])
    print(arguments)
    print(cases)
    if len(cases) > 0 and cases[0] in valid_items:
        case = cases[0]
        job_name = arguments["--job_name"]
        buildid = arguments["--buildid"]
        stage_id = arguments["--stage_id"]
        stage_name = arguments["--stage_name"]
        bootstrap_servers = arguments["--bootstrap_servers"]
        kafka_topic = arguments["--kafka_topic"]
        result_path = arguments.get("--result_path")
        _ci_result = CIResult()
        _ci_result.upload_result(case, job_name, buildid, stage_id, stage_name, bootstrap_servers, kafka_topic, result_path)
    if len(cases[0]) > 0 and cases[0] == "collect_data":
        language = arguments["--language"]
        git_name = arguments["--git_name"]
        git_token = arguments["--git_token"]
        git_commit_id = arguments["--git_commit_id"]
        git_branch_name = arguments["--git_branch_name"]
        eureka_app_name = arguments["--eureka_app_name"]
        job_name = arguments["--job_name"]
        buildid = arguments["--buildid"]
        weixin_token = arguments["--weixin_token"]
        _ci_result = CIResult(language)
        _ci_result.collect_data(job_name, buildid, git_name, git_token=git_token, git_commit_id=git_commit_id, git_branch_name=git_branch_name, eureka_app_name=eureka_app_name, weixin_token=weixin_token)
